﻿using HBase;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.Linq;

namespace Dapper {
    static partial class SqlMapper {
        static int logTime = 500;
        private static T ExecuteScalarImpl<T>(IDbConnection cnn ,ref CommandDefinition command) {
            long begin = DateTime.Now.Ticks;
            lock( cnn ) {
                Action<IDbCommand ,object> paramReader = null;
                object param = command.Parameters;
                if( param != null ) {
                    var identity = new Identity(command.CommandText ,command.CommandType ,cnn ,null ,param.GetType() ,null);
                    paramReader = GetCacheInfo(identity ,command.Parameters  ).ParamReader;
                }
                IDbCommand cmd = null;
                bool wasClosed = cnn.State == ConnectionState.Closed;
                object result;
                try {
                    cmd = command.SetupCommand(cnn ,paramReader);
                    if( wasClosed )
                        cnn.Open();
                    result = cmd.ExecuteScalar();
                    command.OnCompleted();
                } finally {
                    if( wasClosed )
                        cnn.Close();
                    if( cmd != null )
                        cmd.Dispose();
                }
                int between = ( int ) (DateTime.Now.Ticks - begin) / 10000;
                if( between > logTime ) {
                    LoggerCommon.consol(InfoCode.consle_sql_dpbExec, "dpbExec" + command.CommandText + between);
                }
                return Parse<T>(result);
            }
        }
        private static IEnumerable<T> QueryImpl<T>(this IDbConnection cnn ,CommandDefinition command ,Type effectiveType) {//查询接口
            long begin = DateTime.Now.Ticks;
            lock( cnn ) {
                object param = command.Parameters;
                var identity = new Identity(command.CommandText ,command.CommandType ,cnn ,effectiveType ,param == null ? null : param.GetType() ,null);
                var info = GetCacheInfo(identity ,param  );
                IDbCommand cmd = null;
                IDataReader reader = null;
                bool wasClosed = cnn.State == ConnectionState.Closed;
                try {
                    cmd = command.SetupCommand(cnn ,info.ParamReader);
                    if( wasClosed )
                        cnn.Open();
                    reader = cmd.ExecuteReader(wasClosed ? CommandBehavior.CloseConnection | CommandBehavior.SequentialAccess : CommandBehavior.SequentialAccess);
                    wasClosed = false;
                    // *if* the connection was closed and we got this far, then we now have a reader
                    // with the CloseConnection flag, so the reader will deal with the connection; we
                    // still need something in the "finally" to ensure that broken SQL still results
                    // in the connection closing itself
                    var tuple = info.Deserializer;
                    int hash = GetColumnHash(reader);
                    if( tuple.Func == null || tuple.Hash != hash ) {
                        if( reader.FieldCount == 0 )
                            yield break;
                        tuple = info.Deserializer = new DeserializerState(hash ,GetDeserializer(effectiveType ,reader ,0 ,-1 ,false));
                      
                    }
                    var func = tuple.Func;
                    var convertToType = Nullable.GetUnderlyingType(effectiveType) ?? effectiveType;
                    while( reader.Read() ) {
                        object val = func(reader);

                        if( val == null ) {
                            yield return default(T);
                        } else if( val == null || val is T ) {
                            yield return ( T ) val;
                        } else {
                            yield return ( T ) Convert.ChangeType(val ,convertToType ,CultureInfo.InvariantCulture);
                        }
                    }
                    while( reader.NextResult() ) {
                    }
                    // happy path; close the reader cleanly - no
                    // need for "Cancel" etc
                    reader.Dispose();
                    reader = null;
                    command.OnCompleted();
                } finally {
                    if( reader != null ) {
                        if (!reader.IsClosed) {
                            try {
                                reader.Close();
                            } catch { /* don't spoil the existing exception */ }
                            try {
                                cmd.Cancel();
                            } catch { /* don't spoil the existing exception */ }
                        }

                           
                        reader.Dispose();
                    }
                    if( wasClosed )
                        cnn.Close();
                    if( cmd != null )
                        cmd.Dispose();
                }
                int between = ( int ) (DateTime.Now.Ticks - begin) / 10000;
                if( between > logTime ) {
                    LoggerCommon.consol(InfoCode.consle_sql_dpbQuery, "dpbQuery" + command.CommandText + between);
                }
            }
        }
        public static Page<T> QueryMultiple<T>(//分页查询
         this IDbConnection cnn ,string sql ,object param1 = null ,IDbTransaction transaction = null ,int? commandTimeout = null ,CommandType? commandType = null
) {
            long begin = DateTime.Now.Ticks;
            var command = new CommandDefinition(sql ,( object ) param1 ,transaction ,commandTimeout ,commandType );
            lock( cnn ) {
                object param = command.Parameters;
                Identity identity = new Identity(command.CommandText ,command.CommandType ,cnn ,typeof(GridReader) ,param == null ? null : param.GetType() ,null);
                CacheInfo info = GetCacheInfo(identity ,param );
                IDbCommand cmd = null;
                IDataReader reader = null;
                bool wasClosed = cnn.State == ConnectionState.Closed;
                try {
                    if( wasClosed )
                        cnn.Open();
                    cmd = command.SetupCommand(cnn ,info.ParamReader);
                    reader = cmd.ExecuteReader(wasClosed ? CommandBehavior.CloseConnection | CommandBehavior.SequentialAccess : CommandBehavior.SequentialAccess);
                    var ds = new GridReader(cmd ,reader ,identity ,command.Parameters as DynamicParameters  );
                    cmd = null; // now owned by result
                    List<T> it = ds.Read<T>().ToList();
                    object c = ds.Read<int>().First();
                    int count = int.Parse(c.ToString());
                    ds.Dispose();
                    ds = null;
                    Page<T> page = new Page<T>();
                    page.totalSize = count;
                    page.content = it;
                    int between = ( int ) (DateTime.Now.Ticks - begin) / 10000;
                    if( between > logTime ) {
                        LoggerCommon.consol(InfoCode.consle_sql_dpbMultQuery, "dpbMultQuery" + command.CommandText + between);
                    }
                    return page;
                } finally {
                    if( reader != null ) {
                        if( !reader.IsClosed) {

                            try {
                                reader.Close();
                            } catch { /* don't spoil the existing exception */ }
                            try {
                                if (cmd != null) {
                                    cmd.Cancel();
                                }

                            } catch { /* don't spoil the existing exception */ }
                        }
                           
                        reader.Dispose();
                    }
                    if( cmd != null )
                        cmd.Dispose();
                    if( wasClosed )
                        cnn.Close();
                  
                }
            }
        }
        private static int ExecuteImpl(this IDbConnection cnn ,ref CommandDefinition command) {
            lock( cnn ) {
                try {
                    object param = command.Parameters;
                    IEnumerable multiExec = GetMultiExec(param);
                    if (multiExec != null) {
                        return ExecuteCommand2(cnn, ref command, multiExec);
                    }
                    CacheInfo info = null;
                    // nice and simple
                    if (param != null) {
                        Identity identity = new Identity(command.CommandText, command.CommandType, cnn, null, param.GetType(), null);
                        info = GetCacheInfo(identity, param);
                    }
                    return ExecuteCommand(cnn, ref command, param == null ? null : info.ParamReader);
                } catch  {
                    cnn.Close();
                    throw;
                }
               
            }
        }
        private static int ExecuteCommand2(IDbConnection cnn ,ref CommandDefinition command ,IEnumerable multiExec) {
            bool isFirst = true;
            int total = 0;
            bool wasClosed = cnn.State == ConnectionState.Closed;
            CacheInfo info = null;

            try {
                if( wasClosed )
                    cnn.Open();
                using( var cmd = command.SetupCommand(cnn ,null) ) {
                    string masterSql = null;
                    foreach( var obj in multiExec ) {
                        if( isFirst ) {
                            masterSql = cmd.CommandText;
                            isFirst = false;
                            Identity identity = new Identity(command.CommandText ,cmd.CommandType ,cnn ,null ,obj.GetType() ,null);
                            info = GetCacheInfo(identity ,obj  );
                        } else {
                            cmd.CommandText = masterSql; // because we do magic replaces on "in" etc
                            cmd.Parameters.Clear(); // current code is Add-tastic
                        }
                        info.ParamReader(cmd ,obj);
                        total += cmd.ExecuteNonQuery();
                    }
                }
                command.OnCompleted();
            } finally {
                if( wasClosed )
                    cnn.Close();
            }
            return total;
        }
        private static int ExecuteCommand(IDbConnection cnn ,ref CommandDefinition command ,Action<IDbCommand ,object> paramReader) {
            long begin = DateTime.Now.Ticks;
            IDbCommand cmd = null;
            bool wasClosed = cnn.State == ConnectionState.Closed;
            try {
                cmd = command.SetupCommand(cnn, paramReader);
                if (wasClosed) {
                    cnn.Open();

                }

                int result = cmd.ExecuteNonQuery();
                command.OnCompleted();
                int between = (int)(DateTime.Now.Ticks - begin) / 10000;
                if (between > logTime) {
                    LoggerCommon.consol(InfoCode.consle_sql_dpbNoQuerc, "dpbNoQuery" + command.CommandText + between);
                }
                return result;
            } finally {
                if (wasClosed)
                    cnn.Close();
                if (cmd != null)
                    cmd.Dispose();
            }
        }
    }
}
